package com.dianrong.common.uniauth.common.server.cxf.client;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.ext.Provider;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public final class ClientFilterSingleton {
    private static UniauthCxfClientFilter instance = new UniauthCxfClientFilter();
    private static final AtomicBoolean propSetOnce = new AtomicBoolean(false);

    public static ClientRequestFilter getInstance() {
        return instance;
    }

    // 只能被调用一次
    public static void propSetInvoke(List<HeaderProducer> producers) {
        if (propSetOnce.compareAndSet(false, true)) {
            if (producers != null) {
                instance.setProducers(producers);
            }
        }
    }

    @Provider
    private static final class UniauthCxfClientFilter implements ClientRequestFilter {
        private Set<HeaderProducer> producers;

        public UniauthCxfClientFilter() {
            this(new ArrayList<HeaderProducer>());
        }

        public UniauthCxfClientFilter(List<HeaderProducer> producers) {
            setProducers(producers);
        }

        @Override
        public void filter(ClientRequestContext requestContext) throws IOException {
            for (HeaderProducer producer : this.producers) {
                String key = producer.key();
                try {
                    String value = producer.produce();
                    if (key != null && value != null) {
                        requestContext.getStringHeaders().add(key, value);
                    }
                } catch (Exception ex) {
                    log.error("add header [" + key + "] failed", ex);
                }
            }
        }

        // sorted list
        public void setProducers(List<HeaderProducer> producers) {
            if (producers == null) {
                return;
            }
            producers = new ArrayList<HeaderProducer>(producers);
            Collections.sort(producers, new Comparator<HeaderProducer>() {
                @Override
                public int compare(HeaderProducer o1, HeaderProducer o2) {
                    if (o2.getOrder() > o1.getOrder()) {
                        return 1;
                    }
                    if (o2.getOrder() == o1.getOrder()) {
                        return 0;
                    }
                    return -1;
                }
            });
            Map<String, HeaderProducer> _map = new HashMap<String, HeaderProducer>();
            for (HeaderProducer hp : producers) {
                _map.put(hp.key(), hp);
            }
            this.producers = new HashSet<HeaderProducer>(_map.values());
        }
    }
}
